#if !defined(IMGLIB_BMPCOMB_H)
#define IMGLIB_BMPCOMB_H

namespace img {

struct bit_or {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x | y); }
};

struct bit_or_not {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x | (~y)); }
};

struct bit_and {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x & y); }
};

struct bit_and_not {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x & (~y)); }
};

struct bit_not_and {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>((~x) & y); }
};

struct bit_not_and_not {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>((~x) & (~y)); }
};

struct bit_xor {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x ^ y); }
};

struct bit_xor_not {
unsigned char operator()(const unsigned char & x, const unsigned char & y)
	{ return static_cast<unsigned char>(x ^ (~y)); }
};

inline int ifloor(int x, int d)
{
	if (x >= 0)
		return x / d;
	else
		return x / d - 1;
}

inline int ifrac(int x, int d)
{ 
	return ((x % d) + d) % d;
}

template <class OP>
inline void combine(bitmap & bmp, const bitmap & src, OP & op)
{
	assert( bmp.width() == src.width() && bmp.height() == src.height() );
	combine<OP>( bmp, 0, 0, bmp.width(), bmp.height(), src, 0, 0, op );
}

template <class OP>
void combine(bitmap & bmp, int xd, int yd, int wd, int hd, const bitmap & src, int xs, int ys, OP & op)
{
	// coordinate adjustment
	if (xd < 0) {
		xs -= xd;
		wd += xd;
		xd = 0;
	}
	if (yd < 0) {
		ys -= yd;
		hd += yd;
		yd = 0;
	}
	if (xs < 0) {
		xd -= xs;
		wd += xs;
		xs = 0;
	}
	if (ys < 0) {
		yd -= ys;
		hd += ys;
		ys = 0;
	}
	if (xd + wd > int(bmp._width))
		wd = bmp._width - xd;
	if (xs + wd > int(src._width))
		wd = src._width - xs;
	if (wd <= 0)
		return;
	if (yd + hd > int(bmp._height))
		hd = bmp._height - yd;
	if (ys + hd > int(src._height))
		hd = src._height - ys;
	if (hd <= 0)
		return;

	// bit processing
	int dfirst = ifloor(xd, 8);
	int dlast = ifloor(xd + wd, 8);

	int remfirst = ifrac(xd, 8);
	int remlast = ifrac(xd + wd, 8);
	if (remlast == 0) {
		--dlast;
		remlast = 8;
	}
	assert (dfirst <= dlast && dlast < int(bmp._width+7)/8);

	int sfirst = ifloor(xs - remfirst, 8);
	int slast = ifloor(xs + wd - remlast, 8);
	assert (sfirst <= slast && slast < int(src._width+7)/8);

	int shift = ifrac(xs - xd, 8);

	for (int n = 0; n < hd; ++n) {

		unsigned char * dscan = bmp._scans[yd++];
		unsigned char * sscan = src._scans[ys++];

		int d, s;
		for (d = dfirst, s = sfirst; d <= dlast; ++d, ++s) {

			unsigned char mask = 0xFF;
			if (d == dfirst)
				mask &= (0xFF >> remfirst);
			if (d == dlast)
				mask &= (0xFF << (8 - remlast));

			unsigned char arg1 = dscan[d];
			unsigned char arg2 = 0;
			if (s >= 0)
				arg2 |= (sscan[s] << shift);
			if (s + 1 < int(src._scansize))
				arg2 |= (sscan[s + 1] >> (8 - shift));

			dscan[d] = static_cast<unsigned char>((~mask & dscan[d]) | (mask & op(arg1, arg2)));
		}
	}
}

} //namespace img

#endif // IMGLIB_BMPCOMB_H
